package lxs.swift.instance;


import lxs.swift.instance.action.*;
import lxs.swift.instance.convert.DefaultConverter;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.function.BiFunction;


public interface StructInstance<Key> {
    Object of(Key key);


    void set(Key key, Object value);


    Object delete(Key key);

    void retains(Key... keys);

    void iterate(BiFunction<Key, Object, Boolean> function);


    Actions<Key> action();

    default void copy(Key sourceKey, Key targetKey) {
        Object o = of(sourceKey);
        set(targetKey, o);
    }

    default void move(Key fromKey, Key toKey) {
        set(toKey, delete(fromKey));
    }


    /**
     * 交换两个key的值
     *
     * @param left  left
     * @param right right
     */
    default void exchange(Key left, Key right) {
        Object v = delete(left);
        set(left, of(right));
        set(right, v);
    }

    /**
     * 删除keys中的值
     *
     * @param keys keys
     */

    default void deletes(Key... keys) {
        for (Key key : keys) delete(key);
    }

    default Object read(String chainStr) {
        Chain chain = Chain.parse(chainStr);
        return read(chain);
    }

    default Object read(Chain chain) {
        return chain.read(this);
    }

    default void write(String chainStr, Object value) {
        Chain chain = Chain.parse(chainStr);
        chain.write(this, value);
    }

    default void write(Chain chain, Object value) {
        chain.write(this, value);
    }

    static StructInstance<?> from(Object object) {
        if (object == null) return null;
        if (object instanceof StructInstance) return (StructInstance<?>) object;
        if (object instanceof Collection) {
            List<Object> list = Arrays.asList(((Collection<?>) object).toArray());
            return new StructArray(list);
        }
        if (object.getClass().isArray()) {
            List<Object> list = Arrays.asList(((Object[]) object));
            return new StructArray(list);
        }
        return new HashStruct(object);
    }

    class Actions<Key> {
        private final IntAction<Key> ints;
        private final LongAction<Key> longs;
        private final DoubleAction<Key> doubles;
        private final DateAction<Key> dates;
        private final StringAction<Key> strings;
        private final Action<Key, Boolean> bools;
        private final StructAction<Key> structs;
        private final ArrayAction<Key> arrays;
        private final Action<Key, Object> objects;
        private final StructInstance<Key> instance;

        public Actions(StructInstance<Key> instance) {
            this.ints = new IntAction<>(instance, DefaultConverter.getInstance().intConverter());
            this.longs = new LongAction<>(instance, DefaultConverter.getInstance().longConverter());
            this.doubles = new DoubleAction<>(instance, DefaultConverter.getInstance().doubleConverter());
            this.dates = new DateAction<>(instance, DefaultConverter.getInstance().dateConverter());
            this.strings = new StringAction<>(instance, DefaultConverter.getInstance().stringConverter());
            this.bools = new Action<>(instance, DefaultConverter.getInstance().booleanConverter());
            this.structs = new StructAction<>(instance, DefaultConverter.getInstance().structConverter());
            this.arrays = new ArrayAction<>(instance, DefaultConverter.getInstance().arrayConverter());
            this.objects = new Action<>(instance, o -> o);
            this.instance = instance;
        }

        public IntAction<Key> ints() {
            return ints;
        }

        public LongAction<Key> longs() {
            return longs;
        }

        public DoubleAction<Key> doubles() {
            return doubles;
        }

        public DateAction<Key> dates() {
            return dates;
        }

        public StringAction<Key> strings() {
            return strings;
        }

        public Action<Key, Boolean> bools() {
            return bools;
        }

        public StructAction<Key> structs() {
            return structs;
        }

        public ArrayAction<Key> arrays() {
            return arrays;
        }

        public StructInstance<Key> instance() {
            return instance;
        }

        public Action<Key, Object> objects() {
            return objects;
        }

        public <T extends Enum<T>> EnumAction<Key, T> enums(Class<T> clazz) {
            return new EnumAction<>(instance, DefaultConverter.getInstance().enumConverter(clazz));
        }
    }
}
